home *** CD-ROM | disk | FTP | other *** search
- Path: engnews1.Eng.Sun.COM!taumet!clamage
- From: dirk@becker.adviser.com (Dirk Becker)
- Newsgroups: comp.std.c++
- Subject: Re: class.union questions
- Date: 10 Feb 1996 15:39:23 GMT
- Organization: ?
- Approved: clamage@eng.sun.com (comp.std.c++)
- Message-ID: <v01530501ad3e247b1270@[194.163.74.11]>
- NNTP-Posting-Host: taumet.eng.sun.com
- Mime-Version: 1.0
- Content-Type: text/plain; charset="us-ascii"
- X-Sender: dirk@194.163.74.12
- Cc: kanze@lts.sel.alcatel.de
- Content-Length: 11074
- Originator: clamage@taumet
-
- Sorry to be late with this, but I just learned my newsserver
- does not yet deliver my posts.
-
- The first replies to my (DB) original posting seem to have the same
- objection against my interpretation of b). This is my major problem,
- so I put it first now.
-
- James Kanze (JK) and John Max Skaller (JS) wrote:
-
- From the WP: [class.union]
-
- b) "An object of a class with a non-trivial default constructor
- (_class.ctor_), a non-trivial copy constructor (_class.copy_),
- a non-trivial destructor (_class.dtor_), or a non-trivial copy
- assignment operator (_over.ass_, _class.copy_) cannot be a member
- of a union,"
-
- DB> This is a major reduction of the restrictions found in ARM, where all
- DB> kinds of constructors or assignment operators were excluded. Here is
- DB> also a note to give the explanation by the assumption, any member
- DB> functions and especially assignments would usually expect a correctly
- DB> constructed object.
-
- JS> No. It is merely a more precise statement of the same
- JS> restrictions using more technical terminology.
-
- JK> This is, in fact, exactly the restriction in the ARM. All that has
- JK> changes is the way things are described. In the ARM, classes may or
- JK> may not have constructors (destructors, assignment operators). In the
- JK> current standard, *EVERY* class/struct has a copy constructor, an
- JK> assignment operator and a destructor; the distinction is that some of
- JK> these are trivial.
-
- I still disagree totally. Please tell me what I'm reading wrong.
-
- The ARM (mine is an early edition, 1990) does a complete restriction
- against *any* constructor, destructor or user-defined assignment for
- union member objects. (Section 9.5, page 182). The only explanation for
- this rule was already summarized in my original posting, quoted above;
- it has nothing to do with bitwise copy.
-
- The WP indeed allows member objects with a rich set of constructors
- and assignment operators. It does only <g> cripple the 4 most commonly
- used special member functions, exactly naming them, into trivialness.
- - default constructor
- - destructor
- - copy constructor
- - copy assignment
- I assume this exact enumeration implies all other variants of constructor
- and assignment in member objects are free for user defined versions.
- So the following sample seems perfectly legal with the WP, not with ARM.
- The commented lines are the trivialness-required special member functions.
-
- class Bar {
- public:
- // Bar(); // trivial default constructor
- // Bar(const Bar&); // trivial copy constructor
- // ~Bar(); // trivial destructor
- // Bar& operator = (const Bar&); // trivial copy assignment
-
- // plain member function - no problem
- Bar& operator_assign(const Bar&);
-
- // some other constructors - I see no problem here
- Bar(int);
- Bar(float);
-
- // several other simple assignments are allowed
- Bar& operator = (int);
- Bar& operator = (float);
-
- // these are assignments too, see ARM 5.17 or [expr.ass]
- Bar& operator += (const Bar&);
- Bar& operator -= (const Bar&);
- Bar& operator *= (const Bar&);
- Bar& operator /= (const Bar&);
-
- // ... data members etc.
- };
-
- union Foo {
- Bar bar;
- // ... others
- };
-
- DB> So you can have any special assignment operator or constructor for
- DB> your member objects, any but the most common and useful ones.
-
- JK> Correct. The reason is simple: assignment, copy, default construction
- JK> and destruction of a union must be correctly defined without the
- JK> compiler knowing which element the union actually contains. The set
- JK> of forbidden operators are those that the compiler might be called
- JK> upon to use in implicitly generated code.
-
- But why would you like to provide your own constructor and assignments?
- Maybe because you have added some side effects, like range checking or
- trigger a reference counter.
- If you only use arguments matching your custom routines, this will work
- fine. But if you accidentally assign one Foo.bar to another, your
- side-effects won't take place because the WP prevented your custom copy
- assignment operator.
- It is even impossible to hide the unwanted implicit constructors and
- copy assignment into privacy, to make your compiler produce an error
- message.
-
- Foo foo1;
- Foo foo2;
-
- foo1.bar = 4711; // applies your side-effects
- foo2.bar = foo1.bar; // just trivial assignment here due to WP
-
- -----
-
- a) "A union shall not have base classes. A union shall not be used as
- a base class."
-
- JK> Without this rule, you either break the address rule, or you impose
- JK> some special class layout on unions.
-
- The union has no special class layout? I would say it is very special.
- "a class whose member objects all begin at offset zero"
-
- -----
-
- /* my sample of union inheritance */
-
- JK> This is generally handled better by an abstract base class and
- JK> virtual functions.
-
- Sure, I like polymorphic objects. But if you have a vast amount of
- really small objects you may get tempted to use the reduced footprint
- of a (1 or 2 byte) tag instead of a sizeof(ptr) vptr.
- It is also slightly faster to load these unions from a binary file
- than from a stream, allocate, dispatch and construct every single
- object and retrieve every single member.
-
- -----
-
- /* union with longlong as member */
-
- DB> If you are lucky to have some native long long datatype, then this
- DB> would be legal code. If you already had to implement your own
- DB> class longlong, now you lost the chance to use it (here).
-
- JK> This is a problem, but... how would the compiler know when to call
- JK> the constructor/destructor of your longlong?
-
- Sorry for the wrong sample, longlong may work with trivial c/dtor and
- copy operations. The same is with some class Complex.
-
- Let's reconsider a class "string" and reference-counter features as
- sideeffects. The answer for your question - how would the compiler
- know? - came with my conclusion as quoted below: Just tell him.
- Here the union constructor itself gets responsible for construction as
- the correct union variant - e.g. choose the correct discriminator tag,
- like "empty".
-
- DB> I would generally prefer " ... constructors and destructor of member
- DB> objects are ignored, if not explicitly called by the union's ctor/dtor.
- DB> The union's implicit default copy constructor / assignment does a
- DB> binary copy instead of memberwise copy. Objects <containing a vptr>
- DB> cannot be member of a union" - Please pardon my shortcut on vptrs.
-
- The missing sample for this:
-
- class Member {
- public:
- Member();
- // ...
- };
- class Other {
- public:
- Other();
- // ...
- };
-
- union Union {
- Union();
- Union(int);
- // ...
- Member m;
- Other o;
- };
-
- Union::Union()
- { // call none of the base member constructors
- }
-
- // call Member::Member() using a wellknown notation
- Union::Union(int a)
- : m()
- { }
-
- // deluxe variant - but this would mean a severe extension
- Union::Union(int tag)
- : tag==1 ? m() // call Member::Member()
- : tag==2 ? o() // otherwise call Other::Other()
- { }
-
- -----
-
- c) "A union can be thought of as a class"
-
- DB> Here you accept the implications of 12.8.8 and 12.8.13 on implicitly
- DB> defined copy constructor and copy assignment. The already trivial
- DB> copy constructor/assignment of the member objects will then result
- DB> in one large repetition of copies from and to the same memory locations.
- DB> To avoid this behaviour I would strongly recommend to implement your
- DB> own copy constructor or assignment whenever you use a union, because a
- DB> memberwise copy is usually not desired.
-
- JK> Basically, if you study the restrictions on unions carefully, the
- JK> rules are such that a bitwise copy *must* be legal for assignment and
- JK> copy construction. This is the only type of copy the compiler can
- JK> cope with in the absense of a discriminator to tell it which element
- JK> of the union is valid.
-
- JK> IMHO: if you feel that your union needs a copy constructor or an
- JK> assignment operator, you are probably doing something wrong, in the
- JK> sense that you are trying to use unions in a way that they are not
- JK> intended. (As they are defined in the standard. If they were defined
- JK> differently, they might have different reasonable uses.)
-
- I knew, but some compiler implementors forget their homework here.
- If for any reason (generated source) you have a union with lots of
- members, you may accidentally get lots of redundant code. This is
- another reason for implicit bitwise copy.
-
- And if the trivialness requirement were removed from the copy
- operations, like in my (very draft) replacement text quoted above,
- you would also loose the equivalence.
-
- From a discussion outside this newsgroup I finally got a hint on the
- intention behind the new restrictions: They should ensure a bitwise
- copy of the union will copy the current union member correctly.
- This is achieved by forcing bitwise copy for any potential member.
-
- Why did you then not stay just with the POD union? With the
- differentiation between POD union and non-POD union you already
- got the higher degree of complexity, only to undo this with lots
- of additional words.
-
- My first incident with this problem went what I would call a
- user's way instead of the implementor's sight: How can we use
- it? Instead of "How to infer the implicit implementation?"
- Here I felt invited by the WP to use most of the member object
- assignments and than fell into the trivialness trap.
-
- If your intention is bitwise copy as implicit copy for the union
- itself, why not just call it by its real name instead of hiding
- behind "trivialness"?
- "The implicit union copy constructor and copy assignment apply
- bitwise copy."
- No trivialness restrictions on the member objects!
- If someone then decides to have member objects with other copy
- constructor or assignment semantics, he/she just has to provide
- own union copy operations.
-
- -----
- d)
-
- DB> Why is there no anonymous struct?
-
- JS> The committee (as a body) does not find arguments for
- JS> orthogonal design appealing. It is hard to demonstrate
- JS> why orthogonal design is good to those who have little
- JS> experience with orthogonal systems -- most of us,
- JS> considering the horrid systems we're forced to work with
- JS> most of the time.
-
- JK> Agreed here. Although I really don't see it as worth the bother.
- JK> (I could do without anonymous unions, too.)
-
- You won't need the anonymous struct, because you already said you
- don't use unions.
-
- -----
-
- Some new questions:
-
- e) "At most one of the member objects can be stored in a union at
- any time."
-
- Has anybody already thought about the memberwise implicit operations in
- regard to this rule?
- OK, we'll meet again at the bitwise copy, but this is at least nowhere
- explicitly mentioned in the WP.
-
- -----
-
- f)
-
- Why is [class.mem] 15 restricted to POD structs as union members?
- vptrs are already excluded for union members, and I would still
- ask to exclude them when dropping the trivialness.
-
- ------------------------------------------------------------------
- Dirk Becker dirk@becker.adviser.com
- Harderweg 76, 22549 Hamburg, Germany Tel. +49 40 80783143
-
-
-
- [ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
- Contact address: std-c++-request@ncar.ucar.edu. The moderation policy is
- summarized in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
- ]
-